package jtt808

import (
	"bytes"
	"encoding/binary"
	"log"
	"strings"
)

const (
	StartId = 0x7e
	EndId   = 0x7e
)

type Jtt808 struct {
	Id        byte   //标识
	Head      Head   //消息头  13字节
	Body      []byte //消息体 16字节
	CheckCode byte   //校验码 1字节
	Data      []byte //完整一包

}

func (this *Jtt808) GetJtt808Id() uint16 {
	return this.Head.GetId()
}

func (this *Jtt808) GetData() []byte {
	return this.Data
}

func (this *Jtt808) SetId() {
	this.Id = StartId
}

func (this *Jtt808) GetId() byte {
	return this.Id
}

//func (this *Jtt808) GetHead() Head {
//	return this.Head
//}

func (this *Jtt808) SetBody(body []byte) {
	this.Body = body
}

func (this *Jtt808) GetBody() []byte {
	return this.Body
}

// 设置校验码
func (this *Jtt808) SetCheckCode() {
	headBytes := this.Head.ToBytes()
	code := headBytes[0]
	for _, v := range headBytes[1:] {
		code ^= v
	}
	for _, v := range this.Body {
		code ^= v
	}
	this.CheckCode = code
}

func (this *Jtt808) GetCheckCode() byte {
	return this.CheckCode
}

func (this *Jtt808) ToBytes() []byte {
	var data []byte
	if len(this.Body) != int(this.Head.BodyLen) {
		log.Println("消息体实际长度计算异常")
	}
	head := this.Head.ToBytes()
	data = append(head, this.Body...)
	data = bytes.ReplaceAll(data, []byte{0x7d}, []byte{0x7d, 0x01}) //先替换0x7d  防止重复替换
	data = bytes.ReplaceAll(data, []byte{0x7e}, []byte{0x7d, 0x02})
	this.Data = append(append([]byte{StartId}, data...), this.GetCheckCode(), EndId)
	return this.Data
}

type Head struct {
	Id        uint16 //消息 ID 2字节
	Attribute uint16 //消息体属性格式 2字节
	Phone     []byte // 6 字节根据安装后终端自身的手机号转换。手机 号不足 12 位，则在前补充数字，大陆手机 号补充数字 0 港澳台则根据其区号进行位 数补充。
	SerialNo  uint16 //消息流水号 2字节  按发送顺序从 0 开始循环累加
	Potting   uint32 //消息包封装项 如果消息体属性中相关标识位确定消息分 包处理，则该项有内容，否则无该项
	PackCut   bool   //是否是长消息 分包处理
	BodyLen   int16  //消息体长度
	Encrypt   int16  //加密信息 0:未加密  4:RSA加密
}

// 消息头 转换为13字节
func (this *Head) ToBytes() (bag []byte) {
	if this.PackCut {
		bag = make([]byte, 16)
	} else {
		bag = make([]byte, 12)
	}

	binary.BigEndian.PutUint16(bag[0:2], this.GetId())
	binary.BigEndian.PutUint16(bag[2:4], this.GetAttribute())
	phone := this.GetPhone()
	copy(bag[4:10], phone[:])
	binary.BigEndian.PutUint16(bag[10:12], this.GetSeriaNo())
	if this.PackCut {
		binary.BigEndian.PutUint32(bag[12:16], this.GetPotting())
	}
	return
}

func (this *Head) GetId() uint16 {
	return this.Id
}

func (this *Head) SetId(id uint16) {
	this.Id = id
}

func (this *Head) GetPackCut() bool {
	return this.PackCut
}

func (this *Head) GetBodyLen() int16 {
	return this.BodyLen
}

func (this *Head) GetEncrypt() int16 {
	return this.Encrypt
}

func (this *Head) GetAttribute() uint16 {
	return this.Attribute
}

/*
*

	bodyLen 消息体长度 10字节(0-9)
	encrypt 是否加密3字节(10-12) 当此三位都为 0，表示消息体不加密 当第 10 位为 1，表示消息体经过 RSA 算法加密
	cut 是否分包 1字节(13) 位为 1 时表示消息体为长消息，进行分包发送处理，具体分包信息由消息包封装项决定；若第 13 位为 0，则消息头中无消息包封装项字段
*/
func (this *Head) SetAttribute(bodyLen, encrypt, cut int16) {
	b := make([]byte, 2)
	bitsBuf := BitsInit(2, b)
	BitsWrite(bitsBuf, 2, uint64(0))        //[14-15] 保留
	BitsWrite(bitsBuf, 1, uint64(cut))      //[13]是否分包 0否 1是
	BitsWrite(bitsBuf, 3, uint64(encrypt))  //[10-12] 是否加密 加密类型
	BitsWrite(bitsBuf, 10, uint64(bodyLen)) //[0-9]消息体长度
	this.Attribute = binary.BigEndian.Uint16(bitsBuf.GetPData())
	this.PackCut = cut == 1
	this.BodyLen = bodyLen
	this.Encrypt = encrypt
}

func (this *Head) GetPhone() []byte {
	return this.Phone
}

func (this *Head) GetPhoneStr() string {
	return Bcd2Number(this.Phone)
}

func (this *Head) SetPhone(phone string) {
	if num := len(phone); num < 12 {
		phone = strings.Repeat("0", 12-num) + phone
	}
	b := Number2bcd(phone)
	b = StrToBytes(string(b), 6)
	//b := hygprs.IntToBytes(18615514235)
	//log.Println("设置得手机号", hex.EncodeToString(b))
	this.Phone = b
}

func (this *Head) GetSeriaNo() uint16 {
	return this.SerialNo
}

func (this *Head) SetSeriaNo(no uint16) {
	this.SerialNo = no
}

func (this *Head) GetPotting() uint32 {
	return this.Potting
}

func (this *Head) SetPotting(allBagNum, bagNo uint16) {
	var b, allBagNumBytes, bagNoBytes []byte
	binary.BigEndian.PutUint16(allBagNumBytes, allBagNum)
	binary.BigEndian.PutUint16(bagNoBytes, bagNo)
	b = append(allBagNumBytes, bagNoBytes...)
	po := binary.BigEndian.Uint32(b)
	this.Potting = po
}

// 冗余bcc校验码 校验
func CheckCodeCheck(data []byte) byte {
	code := data[0]
	for _, b := range data[1:] {
		code ^= b
	}
	return code
}

type Body struct {
}
